class TypeUtils {
    /// 空安全下的类型判断
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    public static is(value: any, clazz: any): boolean {
        // 不支持Map类型，因为JSON.parse不会得到map类型，{}也不是Map类型，map类型直接用Object替换即可
        // Set也不支持，因为JSON.parse也无法得到一个Set
        if (value === null) {
            return clazz === null
        }
        if (value === undefined) {
            return clazz === undefined
        }

        // 说明value肯定不是null或undefined
        if (clazz == null) {
            return false
        }

        if (typeof value === 'string') {
            return clazz == String
        }
        if (typeof value === 'boolean') {
            return clazz == Boolean
        }
        if (typeof value === 'number') {
            return clazz == Number
        }
        if (typeof value === 'bigint') {
            return clazz == BigInt
        }
        if (typeof value === 'symbol') {
            return clazz == Symbol
        }

        // 说明value不是这些类型，但是clazz却是这些类型，因此不匹配
        if ([String, Boolean, Number, BigInt, Symbol].includes(clazz)) {
            return false
        }

        // function判断要在object之前，因为let func = () => 9; func instanceof Function是true，但是func instanceof Object也是true
        // 不过typeof func是'function'
        if (typeof value === 'function') {
            return clazz == Function
        }

        if (clazz == Function) {
            return false
        }

        if (typeof value === 'object') {
            let res = value instanceof clazz
            if (res) {
                return true
            }
            res = TypeUtils.isObjectType(value, clazz)
            if (!res) {
                console.warn(`值：${JSON.stringify(value)}对类型：${clazz}的匹配度很低`)
            }
            return true
        }

        console.error(`存在未匹配的值：${value}--${clazz}`)
        return false
    }

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    public static isObjectType(value: any, clazz: any): boolean {
        const valueKeys = Object.keys(value)
        const clazzKeys = Object.keys(new clazz)
        const distinctKeys = new Set(valueKeys)
        clazzKeys.forEach(e => {
            distinctKeys.add(e)
        })
        const commonKeyCount = valueKeys.length + clazzKeys.length - distinctKeys.size
        if (clazzKeys.length <= 3) {
            if (commonKeyCount >= 1) {
                return true
            } else {
                return false
            }
        } else {
            if (commonKeyCount >= (clazzKeys.length / 2)) {
                return true
            } else {
                return false
            }
        }
    }

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    public static isDate(value: any): boolean {
        if (value instanceof Date) {
            return true
        }

        if (typeof value === 'boolean') {
            return false
        }

        // 不包括小数
        if (Number.isInteger(value)) {
            return true
        }

        if (typeof value === 'string') {
            // 至少要求'2021-07'到月份，
            if (value.length < 7) {
                return false
            }

            // 防止不同浏览器有不同实现，还是try-catch一下
            try {
                const date = new Date(value)
                if (Number.isNaN(date.getTime())) {
                    return false
                }
                return true
            } catch (err) {
                return false
            }
        }
        // 其他类型不支持转换为Date
        return false
    }

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    public static isUndefined(value: any): boolean {
        return value === undefined
    }

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    public static isNull(value: any): boolean {
        return value === null
    }

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    public static isNullable(value: any): boolean {
        return value == null
    }

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    public static transferString(clazz: any): string {
        if (clazz === null) {
            return 'null'
        }
        if (clazz === undefined) {
            return 'undefined'
        }
        if (clazz == String) {
            return 'string'
        }
        if (clazz == Boolean) {
            return 'boolean'
        }
        if (clazz == Number) {
            return 'number'
        }
        if (clazz == BigInt) {
            return 'bigint'
        }
        if (clazz == Symbol) {
            return 'symbol'
        }
        if (clazz == Object) {
            return 'object'
        }

        if (clazz == Array) {
            return 'Array'
        }

        // 这里开始是自定义的类型
        const str = clazz.toString()
        const piece1 = str.split('class ')[1]
        const idx = piece1.indexOf(' ')
        const piece2 = piece1.substring(0, idx)
        return piece2
    }

    /// 注意，这里不支持用来创建string，bool，number等基础类型值，只能用于创建复杂类型对象
    /// 这个方法主要要解决的痛点是创建自定义类型，没有提供构造函数，则无法在创建的同时初始化字段
    /// 用这个方法支持这样创建：let foo = ObjectUtils.create(Foo, {name: 'aa', age: 8})
    // eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-explicit-any
    public static create<T extends Function>(clazz: T, jsonArgs: any): T {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const res = new (clazz as any)
        Object.assign(res, jsonArgs)
        return res
    }
}

const createClass = TypeUtils.create

export { TypeUtils, createClass }
